package com.lyf.boot.utils;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.lyf.boot.base.system.entity.SysRole;
import com.lyf.boot.core.model.LoginUser;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author Luyufan
 * @date 2023/7/3 15:13
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AuthUtil {

    public static final String HEADER_TOKEN_NAME = "Authorization";

    public static final String LOGIN_USER_KEY = "login-user";

    public static final String USER_KEY = "userId";

    public static final String DEPT_KEY = "deptId";

    public static final String CLIENT_KEY = "clientCode";

    public static final String USERNAME_KEY = "username";

    public static final Map<String, String> NOT_LOGIN_EXCEPTION_MSG = MapUtil.builder(new HashMap<String ,String>())
            .put(NotLoginException.NOT_TOKEN, "未能读取到有效 token")
            .put(NotLoginException.INVALID_TOKEN, "token 无效")
            .put(NotLoginException.TOKEN_TIMEOUT, "token 已过期")
            .put(NotLoginException.BE_REPLACED, "token 已被顶下线")
            .put(NotLoginException.KICK_OUT, "token 已被踢下线")
            .put(NotLoginException.TOKEN_FREEZE, "token 已被冻结")
            .put(NotLoginException.NO_PREFIX, "未按照指定前缀提交 token")
            .put("-100", "客户端ID与Token不匹配")
            .build();

    public static void login(LoginUser loginUser, SaLoginModel model){
        SaStorage storage = SaHolder.getStorage();
        storage.set(LOGIN_USER_KEY, loginUser);
        storage.set(USER_KEY, loginUser.getUserId());
        storage.set(DEPT_KEY, loginUser.getDeptId());
        storage.set(USERNAME_KEY, loginUser.getUsername());
        StpUtil.login(loginUser.getUserId(), model.setExtra(USER_KEY, loginUser.getUserId())
                .setExtra(DEPT_KEY, loginUser.getDeptId()));
        setLoginUser(loginUser);
    }

    public static String getToken(){
        return StpUtil.getTokenValue();
    }

    public static Long getUserId(){
        return Convert.toLong(getExtra(USER_KEY));
    }

    public static Long getDeptId(){
        return Convert.toLong(getExtra(DEPT_KEY));
    }

    public static String getUsername(){
       return getLoginUser().getUsername();
    }

    public static List<String> getUserRoles(){
        LoginUser loginUser = getLoginUser();
        if(null == loginUser){
            return null;
        }
        return loginUser.getRoles().stream().map(SysRole::getRoleCode)
                .collect(Collectors.toList());
    }

    public static void setLoginUser(LoginUser loginUser){
        StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
    }

    public static LoginUser getLoginUser(){
        return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> {
            SaSession session = StpUtil.getTokenSession();
            if (ObjectUtil.isNull(session)) {
                return null;
            }
            return session.get(LOGIN_USER_KEY);
        });
    }

    public static LoginUser getLoginUser(String token) {
        SaSession session = StpUtil.getTokenSessionByToken(token);
        if (ObjectUtil.isNull(session)) {
            return null;
        }
        return (LoginUser) session.get(LOGIN_USER_KEY);
    }

    private static Object getExtra(String key) {
        return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key));
    }

    public static Object getStorageIfAbsentSet(String key, Supplier<Object> handle) {
        try {
            Object obj = SaHolder.getStorage().get(key);
            if (ObjectUtil.isNull(obj)) {
                obj = handle.get();
                SaHolder.getStorage().set(key, obj);
            }
            return obj;
        } catch (Exception e) {
            return null;
        }
    }

    public static void logout(){
        StpUtil.getTokenSession().clear();
        StpUtil.logout();
    }

}
